﻿//-----------------------------------------------------------------------------
// <copyright file="BulkOperationPatchHandlersrEF.cs" company=".NET Foundation">
//      Copyright (c) .NET Foundation and Contributors. All rights reserved. 
//      See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Mapping;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;
using Xunit.Sdk;

namespace Microsoft.Test.E2E.AspNet.OData.BulkOperation
{
    internal class EmployeeDBContext : DbContext
    {
        public EmployeeDBContext()
        {

        }

        public EmployeeDBContext(DbContextOptions<EmployeeDBContext> options) : base(options)
        {

        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Friend> Friends { get; set; }

        protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Employee>().HasKey(c => c.ID);
            modelBuilder.Entity<Employee>().Ignore(c => c.SkillSet);
            modelBuilder.Entity<Employee>().Ignore(c => c.NewFriends);
            modelBuilder.Entity<Employee>().Ignore(c => c.UnTypedFriends);
            modelBuilder.Entity<Employee>().Ignore(c => c.InstanceAnnotations);
            modelBuilder.Entity<Employee>().Ignore(c => c.FavoriteSports);

            modelBuilder.Entity<NewFriend>().Ignore(c => c.InstanceAnnotations);
            modelBuilder.Entity<UnTypedFriend>().Ignore(c => c.InstanceAnnotations);

            modelBuilder.Entity<Friend>().HasKey(c => c.Id);

            modelBuilder.Entity<NewOrder>().Ignore(c => c.Container);
            modelBuilder.Entity<NewOrder>().Ignore(c => c.InstanceAnnotations);
            modelBuilder.Entity<MyNewOrder>().Ignore(c => c.Container);
        }
    }

    internal class APIHandlerFactoryEF : ODataAPIHandlerFactory
    {
        EmployeeDBContext dbContext;

        public APIHandlerFactoryEF(IEdmModel model, EmployeeDBContext dbContext) : base(model)
        {
            this.dbContext = dbContext;
        }

        public override IODataAPIHandler GetHandler(ODataPath odataPath)
        {
            if (odataPath != null)
            {
                var pathItems = odataPath;
            }

            return null;
        }

        public override Task<IODataAPIHandler> GetHandlerAsync(ODataPath odataPath)
        {
            return Task.Run(() => GetHandler(odataPath));
        }

        internal class EmployeeEFPatchHandler : ODataAPIHandler<Employee>
        {
            EmployeeDBContext dbContext = null;

            public EmployeeEFPatchHandler(EmployeeDBContext dbContext)
            {
                this.dbContext = dbContext;
            }

            public override ODataAPIResponseStatus TryCreate(IDictionary<string, object> keyValues, out Employee createdObject, out string errorMessage)
            {
                createdObject = null;
                errorMessage = string.Empty;

                try
                {
                    createdObject = new Employee();
                    dbContext.Employees.Add(createdObject);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryDelete(IDictionary<string, object> keyValues, out string errorMessage)
            {
                errorMessage = string.Empty;

                try
                {
                    var id = keyValues.First().Value.ToString();
                    var customer = dbContext.Employees.First(x => x.ID == Int32.Parse(id));

                    dbContext.Employees.Remove(customer);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryGet(IDictionary<string, object> keyValues, out Employee originalObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;
                originalObject = null;

                try
                {
                    var id = keyValues["ID"].ToString();
                    originalObject = dbContext.Employees.First(x => x.ID == Int32.Parse(id));

                    if (originalObject == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }
                }
                catch (Exception ex)
                {
                    errorMessage = null;
                    status = ODataAPIResponseStatus.Failure;
                    errorMessage = ex.Message;
                }

                return status;
            }

            public override IODataAPIHandler GetNestedHandler(Employee parent, string navigationPropertyName)
            {
                switch (navigationPropertyName)
                {
                    case "Friends":
                        return new FriendEFPatchHandler(parent);
                    case "NewFriends":
                        return new NewFriendEFPatchHandler(parent);
                    default:
                        return null;
                }
            }

            public override ODataAPIResponseStatus TryAddRelatedObject(Employee resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryCreateAsync(IDictionary<string, object> keyValues, out Employee createdObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                createdObject = null;
                errorMessage = string.Empty;

                try
                {
                    createdObject = new Employee();
                    dbContext.Employees.Add(createdObject);

                    status = ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    status = ODataAPIResponseStatus.Failure;
                }

                return Task.FromResult(status);
            }

            public override Task<ODataAPIResponseStatus> TryGetAsync(IDictionary<string, object> keyValues, out Employee originalObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;
                originalObject = null;

                try
                {
                    var id = keyValues["ID"].ToString();
                    originalObject = dbContext.Employees.First(x => x.ID == Int32.Parse(id));

                    if (originalObject == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }
                }
                catch (Exception ex)
                {
                    errorMessage = null;
                    status = ODataAPIResponseStatus.Failure;
                    errorMessage = ex.Message;
                }

                return Task.FromResult(status);
            }

            public override Task<ODataAPIResponseStatus> TryDeleteAsync(IDictionary<string, object> keyValues, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;

                try
                {
                    var id = keyValues.First().Value.ToString();
                    var customer = dbContext.Employees.First(x => x.ID == Int32.Parse(id));

                    dbContext.Employees.Remove(customer);

                    status = ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    status = ODataAPIResponseStatus.Failure;
                }

                return Task.FromResult(status);
            }

            public override Task<ODataAPIResponseStatus> TryAddRelatedObjectAsync(Employee resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override IODataAPIHandler GetNestedHandlerAsync(Employee parent, string navigationPropertyName)
            {
                switch (navigationPropertyName)
                {
                    case "Friends":
                        return (IODataAPIHandler)Task.FromResult(new FriendEFPatchHandler(parent));
                    case "NewFriends":
                        return (IODataAPIHandler)Task.FromResult(new NewFriendEFPatchHandler(parent));
                    default:
                        return null;
                }
            }
        }

        internal class FriendEFPatchHandler : ODataAPIHandler<Friend>
        {
            Employee employee;
            public FriendEFPatchHandler(Employee employee)
            {
                this.employee = employee;
            }

            public override ODataAPIResponseStatus TryCreate(IDictionary<string, object> keyValues, out Friend createdObject, out string errorMessage)
            {
                createdObject = null;
                errorMessage = string.Empty;

                try
                {
                    createdObject = new Friend();
                    employee.Friends.Add(createdObject);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryDelete(IDictionary<string, object> keyValues, out string errorMessage)
            {
                errorMessage = string.Empty;

                try
                {
                    var id = keyValues.First().Value.ToString();
                    var friend = employee.Friends.First(x => x.Id == Int32.Parse(id));

                    employee.Friends.Remove(friend);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryGet(IDictionary<string, object> keyValues, out Friend originalObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;
                originalObject = null;

                try
                {
                    var id = keyValues["Id"].ToString();
                    if (employee.Friends == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }
                    else
                    {
                        originalObject = employee.Friends.FirstOrDefault(x => x.Id == Int32.Parse(id));
                    }

                    if (originalObject == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }

                }
                catch (Exception ex)
                {
                    status = ODataAPIResponseStatus.Failure;
                    errorMessage = ex.Message;
                }

                return status;
            }

            public override IODataAPIHandler GetNestedHandler(Friend parent, string navigationPropertyName)
            {
                return new OrderEFPatchHandler(parent);
            }

            public override ODataAPIResponseStatus TryAddRelatedObject(Friend resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryCreateAsync(IDictionary<string, object> keyValues, out Friend createdObject, out string errorMessage)
            {
                createdObject = null;
                errorMessage = null;

                return Task.Run(() => TryCreate(keyValues, out Friend createdObject, out string errorMessage));
            }

            public override Task<ODataAPIResponseStatus> TryGetAsync(IDictionary<string, object> keyValues, out Friend originalObject, out string errorMessage)
            {
                originalObject = null;
                errorMessage = null;

                return Task.Run(() => TryGet(keyValues, out Friend originalObject, out string errorMessage));
            }

            public override Task<ODataAPIResponseStatus> TryDeleteAsync(IDictionary<string, object> keyValues, out string errorMessage)
            {
                errorMessage = null;

                return Task.Run(() => TryDeleteAsync(keyValues, out string errorMessage));
            }

            public override Task<ODataAPIResponseStatus> TryAddRelatedObjectAsync(Friend resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override IODataAPIHandler GetNestedHandlerAsync(Friend parent, string navigationPropertyName)
            {
                return (IODataAPIHandler)Task.Run(() => GetNestedHandler(parent, navigationPropertyName));
            }
        }

        internal class NewFriendEFPatchHandler : ODataAPIHandler<NewFriend>
        {
            Employee employee;
            public NewFriendEFPatchHandler(Employee employee)
            {
                this.employee = employee;
            }

            public override ODataAPIResponseStatus TryCreate(IDictionary<string, object> keyValues, out NewFriend createdObject, out string errorMessage)
            {
                createdObject = null;
                errorMessage = string.Empty;

                try
                {
                    createdObject = new NewFriend();
                    employee.NewFriends.Add(createdObject);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryDelete(IDictionary<string, object> keyValues, out string errorMessage)
            {
                errorMessage = string.Empty;

                try
                {
                    var id = keyValues.First().Value.ToString();
                    var friend = employee.NewFriends.First(x => x.Id == Int32.Parse(id));

                    employee.NewFriends.Remove(friend);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryGet(IDictionary<string, object> keyValues, out NewFriend originalObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;
                originalObject = null;

                try
                {
                    var id = keyValues["Id"].ToString();
                    originalObject = employee.NewFriends.First(x => x.Id == Int32.Parse(id));

                    if (originalObject == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }

                }
                catch (Exception ex)
                {
                    status = ODataAPIResponseStatus.Failure;
                    errorMessage = ex.Message;
                }

                return status;
            }

            public override IODataAPIHandler GetNestedHandler(NewFriend parent, string navigationPropertyName)
            {
                return null;
            }

            public override ODataAPIResponseStatus TryAddRelatedObject(NewFriend resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryCreateAsync(IDictionary<string, object> keyValues, out NewFriend createdObject, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryGetAsync(IDictionary<string, object> keyValues, out NewFriend originalObject, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryDeleteAsync(IDictionary<string, object> keyValues, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryAddRelatedObjectAsync(NewFriend resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override IODataAPIHandler GetNestedHandlerAsync(NewFriend parent, string navigationPropertyName)
            {
                throw new NotImplementedException();
            }
        }

        internal class OrderEFPatchHandler : ODataAPIHandler<Order>
        {
            Friend friend;
            public OrderEFPatchHandler(Friend friend)
            {
                this.friend = friend;
            }

            public override ODataAPIResponseStatus TryCreate(IDictionary<string, object> keyValues, out Order createdObject, out string errorMessage)
            {
                createdObject = null;
                errorMessage = string.Empty;

                try
                {
                    createdObject = new Order();
                    friend.Orders.Add(createdObject);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryDelete(IDictionary<string, object> keyValues, out string errorMessage)
            {
                errorMessage = string.Empty;

                try
                {
                    var id = keyValues.First().Value.ToString();
                    var order = friend.Orders.First(x => x.Id == Int32.Parse(id));

                    friend.Orders.Remove(order);

                    return ODataAPIResponseStatus.Success;
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;

                    return ODataAPIResponseStatus.Failure;
                }
            }

            public override ODataAPIResponseStatus TryGet(IDictionary<string, object> keyValues, out Order originalObject, out string errorMessage)
            {
                ODataAPIResponseStatus status = ODataAPIResponseStatus.Success;
                errorMessage = string.Empty;
                originalObject = null;

                try
                {
                    var id = keyValues["Id"].ToString();
                    originalObject = friend.Orders.First(x => x.Id == Int32.Parse(id));

                    if (originalObject == null)
                    {
                        status = ODataAPIResponseStatus.NotFound;
                    }
                }
                catch (Exception ex)
                {
                    status = ODataAPIResponseStatus.Failure;
                    errorMessage = ex.Message;
                }

                return status;
            }

            public override IODataAPIHandler GetNestedHandler(Order parent, string navigationPropertyName)
            {
                throw new NotImplementedException();
            }

            public override ODataAPIResponseStatus TryAddRelatedObject(Order resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryCreateAsync(IDictionary<string, object> keyValues, out Order createdObject, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryGetAsync(IDictionary<string, object> keyValues, out Order originalObject, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryDeleteAsync(IDictionary<string, object> keyValues, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override Task<ODataAPIResponseStatus> TryAddRelatedObjectAsync(Order resource, out string errorMessage)
            {
                throw new NotImplementedException();
            }

            public override IODataAPIHandler GetNestedHandlerAsync(Order parent, string navigationPropertyName)
            {
                throw new NotImplementedException();
            }
        }
    }
}
